package com.zf.developaccumulate.utils;

import com.zf.developaccumulate.entity.MonthWeekDataVO;
import com.zf.developaccumulate.entity.MonthWeekRelationVO;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.BeanUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 将月数据按照周的天数占月天数的比例拆分到周
 * @author Zhou Feng
 */
public class SplitMonthToWeekUtils {
    public static void main(String[] args) {
        String monthCode = "202309";
        // 构造一份月数据
        MonthWeekDataVO monthWeekDataVO = new MonthWeekDataVO(monthCode, "", -177);
        // 获取月对应的周
        List<MonthWeekRelationVO> weekDataByMonth = getWeekDataByMonth(monthCode);
        // 计算周的比例
        Map<String, BigDecimal> weekRateMap = getWeekRate(weekDataByMonth);
        // 拆分数据
        List<MonthWeekDataVO> res = splitMonthByWeekRate(monthWeekDataVO, weekRateMap);
        System.out.println(res);

    }


    /**
     * 按照比例拆分月份数据:
     * 最后一周=月数量-前面拆分的数量的和
     *
     */
    public static List<MonthWeekDataVO> splitMonthByWeekRate(MonthWeekDataVO tempDataVO, Map<String, BigDecimal> rateMap) {
        // 结果
        ArrayList<MonthWeekDataVO> splitResList = new ArrayList<>();
        // 构造结果数据
        for (Map.Entry<String, BigDecimal> entry : rateMap.entrySet()) {
            MonthWeekDataVO tmp = new MonthWeekDataVO();
            BeanUtils.copyProperties(tempDataVO, tmp);
            tmp.setPeriodWeek(entry.getKey());
            splitResList.add(tmp);
        }
        // 是否是负数
        boolean isNegative = tempDataVO.getQty() < 0;
        // 总数量
        int total = Math.abs(tempDataVO.getQty());
        // 已经拆分的数量
        int usedQty = 0;
        // 按照比例对数据进行拆分，不拆分最后一个
        for (int i = 0; i < splitResList.size() - 1; i++) {
            MonthWeekDataVO vo = splitResList.get(i);
            // 已经全部拆分完毕，则不再拆分
            if (usedQty >= total) {
                vo.setQty(0);
                continue;
            }
            // 获取周的占比
            BigDecimal rate = MapUtils.getObject(rateMap, vo.getPeriodWeek(), BigDecimal.ZERO);
            // 四舍五入
            int splitQty = BigDecimal.valueOf(total).multiply(rate).setScale(0, RoundingMode.HALF_UP).intValue();
            // 如果拆分的数量总和已经大于 total，则直接赋值剩余数量
            if (usedQty + splitQty >= total) {
                vo.setQty(total - usedQty);
                usedQty = total;
            } else {
                // 如果拆分的数量总和小于 total，则直接赋值拆分数量
                vo.setQty(splitQty);
                usedQty += splitQty;
            }
        }
        // 将剩余的数量，放到最后一个
        MonthWeekDataVO lastVo = splitResList.get(splitResList.size() - 1);
        lastVo.setQty(Math.max(0, total - usedQty));

        // 处理负值
        if (isNegative) {
            for (MonthWeekDataVO item : splitResList) {
                item.setQty(-item.getQty());
            }
        }
        return splitResList;
    }


    /**
     * 计算每个月中周的占比: 保留4位小数
     */
    public static Map<String, BigDecimal> getWeekRate(List<MonthWeekRelationVO> weekList) {
        // 总数量
        BigDecimal sum = new BigDecimal(weekList.stream().mapToInt(MonthWeekRelationVO::getDayOfMonth).sum());
        // 总周数
        BigDecimal count = new BigDecimal(weekList.size());
        LinkedHashMap<String, BigDecimal> weekRateMap = weekList.stream()
                .collect(Collectors.toMap(
                        MonthWeekRelationVO::getPeriodWeek, item -> new BigDecimal(item.getDayOfMonth()).divide(sum, 4, RoundingMode.HALF_UP), (cur, old) -> cur, LinkedHashMap::new)
                );
        // 判断比例是否全都是 0
        boolean isAllZero = weekRateMap.values().stream().noneMatch(item -> item.compareTo(BigDecimal.ZERO) != 0);
        // 如果比例全都是 0 ，则比例均分
        if (isAllZero) {
            weekRateMap = weekList.stream()
                    .collect(Collectors.toMap(
                            MonthWeekRelationVO::getPeriodWeek, item -> new BigDecimal(item.getDayOfMonth()).divide(count, 4, RoundingMode.HALF_UP), (cur, old) -> cur, LinkedHashMap::new)
                    );
        }
        return weekRateMap;
    }


    /**
     * 获取周月比例数据
     */
    public static List<MonthWeekRelationVO> getWeekDataByMonth(String monthCode) {
        ArrayList<MonthWeekRelationVO> res = new ArrayList<>();
        res.add(new MonthWeekRelationVO(monthCode, "W1", 3));
        res.add(new MonthWeekRelationVO(monthCode, "W2", 7));
        res.add(new MonthWeekRelationVO(monthCode, "W3", 7));
        res.add(new MonthWeekRelationVO(monthCode, "W4", 7));
        res.add(new MonthWeekRelationVO(monthCode, "W5", 6));
        return res;
    }

}
